package com.ebraga.trackerclub.android.dao.core;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import android.database.Cursor;

import com.ebraga.trackerclub.android.dao.api.PositionDao;
import com.ebraga.trackerclub.android.dao.entity.PositionAndroid;
import com.ebraga.trackerclub.android.exception.BackofficeDBConnectionException;
import com.ebraga.trackerclub.android.exception.dao.BackofficeDaoException;
import com.ebraga.trackerclub.android.exception.dao.BackofficeIllegalArgumentDaoException;
import com.ebraga.trackerclub.android.util.ISmartServiceConstants;
import com.ebraga.trackerclub.android.util.api.AndroidContentValues;
import com.ebraga.trackerclub.android.util.api.DataAccessObject;

public abstract class PositionDaoImpl implements PositionDao {
	 
	private DataAccessObject dataAccessObject;
	private AndroidContentValues contentValues;
	
	public PositionDaoImpl (DataAccessObject dataAccessObject){
		this.dataAccessObject = dataAccessObject;
	}
	
	public void setContentValues(AndroidContentValues contentValues) {
		this.contentValues = contentValues;
	}
	
	private static final String[] SELECTED_FIELDS = new String[] {
			ISmartServiceConstants.DATABASE.POSITION.FIELD_ID,
			ISmartServiceConstants.DATABASE.POSITION.FIELD_IMEI,
			ISmartServiceConstants.DATABASE.POSITION.FIELD_LATITUDE,
			ISmartServiceConstants.DATABASE.POSITION.FIELD_LONGITUDE,
			ISmartServiceConstants.DATABASE.POSITION.FIELD_ACCURACY,
			ISmartServiceConstants.DATABASE.POSITION.FIELD_PROVIDER,
			ISmartServiceConstants.DATABASE.POSITION.FIELD_CREATE_DATE,
			ISmartServiceConstants.DATABASE.POSITION.FIELD_SYNC_DATE,
			ISmartServiceConstants.DATABASE.POSITION.FIELD_SPEED
			};
	
	@Override
	public void insert(PositionAndroid position)
			throws BackofficeDBConnectionException, BackofficeDaoException {		
		Long ok;
		
		if (position == null){
			throw new BackofficeDaoException("Posição inválida");
		}
		try {
			dataAccessObject.getDB();
		} catch (BackofficeDaoException e) {
			throw new BackofficeDBConnectionException("Erro ao conectar banco de dados", e);
		}
		try {
			validateEntityToPersist(position);
			ok = dataAccessObject.getDB().insert(ISmartServiceConstants.DATABASE.POSITION.TABLE_NAME, createRecord(position));
			if (ok > 0) {
				position.setXidPosition(ok);
			}
		} catch (BackofficeDBConnectionException e) {
			throw new BackofficeDBConnectionException("Erro ao conectar banco de dados", e);
		} catch (Exception e) {
			throw new BackofficeDaoException("Erro ao inserir posição", e);
		}
	}

	@Override
	public List<PositionAndroid> retrievePositionsToSync()
			throws BackofficeDBConnectionException, BackofficeDaoException {
		
		try {
			dataAccessObject.getDB();
		} catch (BackofficeDaoException e) {
			throw new BackofficeDBConnectionException("Erro ao conectar banco de dados");
		}
		
		Cursor cursor = null;
		try{
			String selectionColumns = 
					ISmartServiceConstants.DATABASE.POSITION.FIELD_SYNC_DATE 
					+ " IS NULL AND NOT EXISTS (SELECT "
					+ ISmartServiceConstants.DATABASE.CHECKINOUT.FIELD_POSITION_ID
					+ " FROM "
					+ ISmartServiceConstants.DATABASE.CHECKINOUT.TABLE_NAME
					+ " WHERE "
					+ ISmartServiceConstants.DATABASE.CHECKINOUT.FIELD_SYNC_DATE
					+ " IS NULL AND "
					+ ISmartServiceConstants.DATABASE.CHECKINOUT.FIELD_POSITION_ID
					+ " = "
					+ ISmartServiceConstants.DATABASE.POSITION.FIELD_ID
					+ " )";
			
			cursor = dataAccessObject.getDB().query(
					ISmartServiceConstants.DATABASE.POSITION.TABLE_NAME,
					SELECTED_FIELDS,
					selectionColumns, 
					null, null, null, null);
			
			return getRecordsFromCursor(cursor);
			
		}catch(Exception e){
			throw new BackofficeDaoException("Erro ao executar query", e);
		} finally {
			if(cursor != null)
				cursor.close();
		}
	}
	
	@Override
	public List<PositionAndroid> retrieveCheckInOutPositionsToSync()
			throws BackofficeDBConnectionException, BackofficeDaoException {
		
		try {
			dataAccessObject.getDB();
		} catch (BackofficeDaoException e) {
			throw new BackofficeDBConnectionException("Erro ao conectar banco de dados");
		}
		
		Cursor cursor = null;
		try{
			String selectionColumns = 
					ISmartServiceConstants.DATABASE.POSITION.FIELD_SYNC_DATE 
					+ " IS NULL AND EXISTS (SELECT "
					+ ISmartServiceConstants.DATABASE.CHECKINOUT.FIELD_POSITION_ID
					+ " FROM "
					+ ISmartServiceConstants.DATABASE.CHECKINOUT.TABLE_NAME
					+ " WHERE "
					+ ISmartServiceConstants.DATABASE.CHECKINOUT.FIELD_SYNC_DATE
					+ " IS NULL AND "
					+ ISmartServiceConstants.DATABASE.CHECKINOUT.FIELD_POSITION_ID
					+ " = "
					+ ISmartServiceConstants.DATABASE.POSITION.FIELD_ID
					+ " )";
			
			cursor = dataAccessObject.getDB().query(
					ISmartServiceConstants.DATABASE.POSITION.TABLE_NAME,
					SELECTED_FIELDS,
					selectionColumns, 
					null, null, null, null);
			
			return getRecordsFromCursor(cursor);
			
		}catch(Exception e){
			throw new BackofficeDaoException("Erro ao executar query", e);
		} finally {
			if(cursor != null)
				cursor.close();
		}
	}
	
	@Override
	public void markAsSynchronized(List<PositionAndroid> positionList)
			throws BackofficeDBConnectionException, BackofficeDaoException {
		if(positionList!=null && !positionList.isEmpty()) 
		{	
			for (PositionAndroid position : positionList) {			
				position.setSinchronized(new Date().getTime());
				
				contentValues = createRecord(position);
						
				if (validateEntityToPersist(position)) {
					try{
						dataAccessObject.getDB().update(ISmartServiceConstants.DATABASE.POSITION.TABLE_NAME,
															contentValues,
															ISmartServiceConstants.DATABASE.POSITION.FIELD_ID + " = ? ",
															new String[] { position.getXidPosition().toString() });
						
					} catch(BackofficeDBConnectionException e){
						throw new BackofficeDBConnectionException("Erro ao conectar banco de dados", e);
					} catch(BackofficeDaoException e) {
						throw e;
					} catch(Exception e){
						throw new BackofficeDaoException(e);
					}
				}
			}
		}
	}

	@Override
	public PositionAndroid getLastPositionbyUser(String login)
			throws BackofficeDBConnectionException, BackofficeDaoException , BackofficeIllegalArgumentDaoException {
		
		if (login == null){
			throw new BackofficeIllegalArgumentDaoException("Usuario invalido");
		}
		
		try {
			dataAccessObject.getDB();
		}catch (BackofficeDaoException e){
			throw new BackofficeDBConnectionException("Erro ao conectar banco de dados", e);
		}
		
		Cursor cursor = null;
		try{
			String where = ISmartServiceConstants.DATABASE.POSITION.FIELD_IMEI + " = ? ";
			String[] whereArgs = new String[] {login};
			String orderBy = ISmartServiceConstants.DATABASE.POSITION.FIELD_SYNC_DATE + " DESC";
			
			cursor = dataAccessObject.getDB().query(ISmartServiceConstants.DATABASE.POSITION.TABLE_NAME, SELECTED_FIELDS, where, whereArgs, null, null, orderBy);
		
			PositionAndroid positionAndroid = null;
			List<PositionAndroid> result = getRecordsFromCursor(cursor);
			if (result != null && result.size() > 0) {
				positionAndroid =  result.get(0);
			}
			
			return positionAndroid;
			
		}catch(Exception e){
			throw new BackofficeDaoException("Erro ao executar query", e);
		} finally {
			if(cursor != null)
				cursor.close();
		}
	}
	

	@Override
	public PositionAndroid getMaxSpeedByUser(String login)
			throws BackofficeDBConnectionException, BackofficeDaoException , BackofficeIllegalArgumentDaoException {
		
		if (login == null){
			throw new BackofficeIllegalArgumentDaoException("Usuario invalido");
		}
		
		try {
			dataAccessObject.getDB();
		}catch (BackofficeDaoException e){
			throw new BackofficeDBConnectionException("Erro ao conectar banco de dados", e);
		}
		
		Cursor cursor = null;
		try{
			String where = ISmartServiceConstants.DATABASE.POSITION.FIELD_IMEI + " = ? ";
			String[] whereArgs = new String[] {login};
			String orderBy = ISmartServiceConstants.DATABASE.POSITION.FIELD_SPEED + " DESC";
			
			cursor = dataAccessObject.getDB().query(ISmartServiceConstants.DATABASE.POSITION.TABLE_NAME, SELECTED_FIELDS, where, whereArgs, null, null, orderBy);
		
			PositionAndroid positionAndroid = null;
			List<PositionAndroid> result = getRecordsFromCursor(cursor);
			if (result != null && result.size() > 0) {
				positionAndroid =  result.get(0);
			}
			
			return positionAndroid;
			
		}catch(Exception e){
			throw new BackofficeDaoException("Erro ao executar query", e);
		} finally {
			if(cursor != null)
				cursor.close();
		}
	}	

	private AndroidContentValues createRecord(PositionAndroid positionAndroid) {
		
		contentValues.put(ISmartServiceConstants.DATABASE.POSITION.FIELD_ID, positionAndroid.getXidPosition());
		contentValues.put(ISmartServiceConstants.DATABASE.POSITION.FIELD_IMEI, positionAndroid.getImei());
		contentValues.put(ISmartServiceConstants.DATABASE.POSITION.FIELD_LATITUDE, positionAndroid.getLatitude());
		contentValues.put(ISmartServiceConstants.DATABASE.POSITION.FIELD_LONGITUDE, positionAndroid.getLongitude());
		contentValues.put(ISmartServiceConstants.DATABASE.POSITION.FIELD_ACCURACY, positionAndroid.getAccuracy());
		contentValues.put(ISmartServiceConstants.DATABASE.POSITION.FIELD_PROVIDER, positionAndroid.getProvider());
		contentValues.put(ISmartServiceConstants.DATABASE.POSITION.FIELD_CREATE_DATE, positionAndroid.getDateCreated());
		contentValues.put(ISmartServiceConstants.DATABASE.POSITION.FIELD_SYNC_DATE, positionAndroid.getSinchronized());
		contentValues.put(ISmartServiceConstants.DATABASE.POSITION.FIELD_SPEED, positionAndroid.getSpeed());
		
		return contentValues;
	}
	
	private List<PositionAndroid> getRecordsFromCursor(Cursor cursor) {
		
		List<PositionAndroid> result = new ArrayList<PositionAndroid>();
		
		while (cursor.moveToNext()) {
			
			PositionAndroid positionAndroid = new PositionAndroid();
			positionAndroid.setXidPosition(cursor.getLong(cursor.getColumnIndexOrThrow(ISmartServiceConstants.DATABASE.POSITION.FIELD_ID)));
			positionAndroid.setAccuracy(cursor.getDouble(cursor.getColumnIndexOrThrow(ISmartServiceConstants.DATABASE.POSITION.FIELD_ACCURACY)));
			positionAndroid.setDateCreated(cursor.getLong(cursor.getColumnIndexOrThrow(ISmartServiceConstants.DATABASE.POSITION.FIELD_CREATE_DATE)));
			positionAndroid.setImei(cursor.getString(cursor.getColumnIndexOrThrow(ISmartServiceConstants.DATABASE.POSITION.FIELD_IMEI)));
			positionAndroid.setLatitude(cursor.getDouble(cursor.getColumnIndexOrThrow(ISmartServiceConstants.DATABASE.POSITION.FIELD_LATITUDE)));
			positionAndroid.setLongitude(cursor.getDouble(cursor.getColumnIndexOrThrow(ISmartServiceConstants.DATABASE.POSITION.FIELD_LONGITUDE)));
			positionAndroid.setProvider(cursor.getString(cursor.getColumnIndexOrThrow(ISmartServiceConstants.DATABASE.POSITION.FIELD_PROVIDER)));
			positionAndroid.setSinchronized(cursor.getLong(cursor.getColumnIndexOrThrow(ISmartServiceConstants.DATABASE.POSITION.FIELD_SYNC_DATE)));
			positionAndroid.setSpeed(cursor.getDouble(cursor.getColumnIndexOrThrow(ISmartServiceConstants.DATABASE.POSITION.FIELD_SPEED)));

			result.add(positionAndroid);
		}

		return result;
	}	
	
	private boolean validateEntityToPersist(PositionAndroid position) throws BackofficeIllegalArgumentDaoException {
		
		if (position.getImei()      == null || 
			position.getLatitude()    == null || 
			position.getLongitude()   == null ||
			position.getDateCreated() == null){
			throw new BackofficeIllegalArgumentDaoException("Posição invalida para atualização");			
		}
		return true;
	}	
	
}